﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Evaluation;
using Microsoft.Xna.Framework.Content;
using SceneEngine;
using Microsoft.Xna.Framework.Graphics;

namespace SceneEditor
{
    /// <summary>
    /// A set of global properties and helpers relevant to the editor.
    /// </summary>
    public static class EditorGlobals
    {
        private static Project contentProject;

        public static ModelManager ModelManager;

        /// <summary>
        /// The file path of the content directory.
        /// </summary>
        public static string ContentPath;
        /// <summary>
        /// The file path of the content project.
        /// </summary>
        public static string ContentProjectPath;
        /// <summary>
        /// Loads content built by the editor.
        /// </summary>
        public static ContentManager BuiltContent;
        /// <summary>
        /// Loads content belonging to the editor.
        /// </summary>
        public static ContentManager EditorContent;

        public static readonly string XnaVersion = ", Version=4.0.0.0, PublicKeyToken=842cf8be1de50553";

        /// <summary>
        /// A set of assemblies used to build Xna content.
        /// </summary>
        public static readonly string[] PipelineAssemblies =
        {
            "Microsoft.Xna.Framework.Content.Pipeline.FBXImporter" + XnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.XImporter" + XnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.TextureImporter" + XnaVersion,
            "Microsoft.Xna.Framework.Content.Pipeline.EffectImporter" + XnaVersion,
            AppDomain.CurrentDomain.BaseDirectory + "EnginePipeline.dll",
        };

        /// <summary>
        /// Initialzes global properties relevant to the editor.
        /// </summary>
        public static void Initialize(string contentProjPath)
        {
            ContentPath = Path.GetDirectoryName(contentProjPath);
            ContentProjectPath = contentProjPath;

            contentProject = EditorGlobals.GetProject(contentProjPath);

            if (ContainsPipelineReference() == false)
            {
                MessageBoxHelper.ShowPipelineReferenceMissing();
            }
        }

        /// <summary>
        /// Adds a new content file to the content project.
        /// </summary>
        public static void AddContentItem(ProjectItem projectItem, string relativeDirectory, bool saveProject)
        {
            string name = projectItem.GetMetadataValue("Name");
            string link = projectItem.GetMetadataValue("Link");
            string path = Path.Combine(relativeDirectory, link);

            ProjectItem newItem = contentProject.AddItem("Compile", name + Path.GetExtension(link))[0];

            newItem.SetMetadataValue("Name", Path.GetFileNameWithoutExtension(name));
            newItem.SetMetadataValue("Importer", projectItem.GetMetadataValue("Importer"));
            newItem.SetMetadataValue("Processor", projectItem.GetMetadataValue("Processor"));

            if (saveProject)
            {
                contentProject.Save();
            }
        }
        /// <summary>
        /// Adds a new content file to the content project.
        /// </summary>
        public static void AddContentItem(string name, string path, string importer, string processor)
        {
            ProjectItem newItem = contentProject.AddItem("Compile", path)[0];

            newItem.SetMetadataValue("Name", name);

            if (!string.IsNullOrEmpty(importer))
                newItem.SetMetadataValue("Importer", importer);

            if (!string.IsNullOrEmpty(processor))
                newItem.SetMetadataValue("Processor", processor);

            contentProject.Save();
        }
        /// <summary>
        /// Adds a new file to the content project.
        /// </summary>
        public static void AddItem(string name, string path)
        {
            ProjectItem newItem = contentProject.AddItem("None", path)[0];

            newItem.SetMetadataValue("Name", name);
            newItem.SetMetadataValue("Importer", null);
            newItem.SetMetadataValue("Processor", null);

            contentProject.Save();
        }
        /// <summary>
        /// Adds a new file to the content project.
        /// </summary>
        public static void AddFolderItem(string path)
        {
            if (ContainsFolderItem(path) == false)
            {
                ProjectItem newItem = contentProject.AddItem("Folder", path)[0];
                contentProject.Save();
            }
        }

        /// <summary>
        /// Adds a set of content files to the content project.
        /// </summary>
        public static void AddContentItems(List<ProjectItem> projectItems, string relativeDirectory)
        {
            foreach (ProjectItem projectItem in projectItems)
            {
                AddContentItem(projectItem, relativeDirectory, false);
            }

            contentProject.Save();
        }

        public static bool ContainsFolderItem(string path)
        {
            foreach (ProjectItem projectItem in contentProject.GetItems("Folder"))
            {
                if (projectItem.EvaluatedInclude == path)
                {
                    return true;
                }
            }

            return false;
        }
        public static bool ContainsContentItem(string relativeFileName)
        {
            foreach (ProjectItem projectItem in contentProject.GetItems("Compile"))
            {
                if (projectItem.EvaluatedInclude == relativeFileName)
                {
                    return true;
                }
            }

            return false;
        }
        public static bool ContainsPipelineReference()
        {
            foreach (ProjectItem projectItem in contentProject.GetItems("Reference"))
            {
                if (projectItem.EvaluatedInclude.Contains("EnginePipeline"))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Removes a content file from the content project.
        /// </summary>
        public static void RemoveContentItem(string relativeFileName)
        {
            foreach (ProjectItem projectItem in contentProject.GetItems("Compile"))
            {
                if (projectItem.EvaluatedInclude == relativeFileName)
                {
                    contentProject.RemoveItem(projectItem);
                    break;
                }
            }

            contentProject.Save();
        }
        /// <summary>
        /// Removes a content file from the content project.
        /// </summary>
        public static void RemoveFolderItem(string path)
        {
            foreach (ProjectItem projectItem in contentProject.GetItems("Folder"))
            {
                if (projectItem.EvaluatedInclude == path)
                {
                    contentProject.RemoveItem(projectItem);
                    break;
                }
            }

            contentProject.Save();
        }
        /// <summary>
        /// Removes a content file from the content project.
        /// </summary>
        public static void RemoveContentItem(string relativeFileName, string extension)
        {
            foreach (ProjectItem projectItem in contentProject.GetItems("Compile"))
            {
                if (projectItem.EvaluatedInclude == relativeFileName + extension.ToLower() ||
                    projectItem.EvaluatedInclude == relativeFileName + extension.ToUpper())
                {
                    contentProject.RemoveItem(projectItem);
                    break;
                }
            }

            contentProject.Save();
        }

        public static System.Drawing.Image Texture2DToImage(Texture2D texture, int size)
        {
            if (texture == null)
            {
                return null;
            }

            if (texture.IsDisposed)
            {
                return null;
            }

            System.Drawing.Image image = null;

            using (MemoryStream memoryStream = new MemoryStream())
            {
                texture.SaveAsPng(memoryStream, size, size);

                memoryStream.Seek(0, SeekOrigin.Begin);

                image = System.Drawing.Bitmap.FromStream(memoryStream);

                memoryStream.Close();
            }

            return image;
        }

        public static void ImageToTexture2D(System.Drawing.Image image,
                                 ref Texture2D texture)
        {
            if (image == null)
            {
                return;
            }

            if (texture == null || texture.IsDisposed ||
                texture.Width != image.Width ||
                texture.Height != image.Height ||
                texture.Format != SurfaceFormat.Color)
            {
                if (texture != null && !texture.IsDisposed)
                {
                    texture.Dispose();
                }

                texture = new Texture2D(EngineGlobals.GraphicsDevice, image.Width, 
                    image.Height, false, SurfaceFormat.Color);
            }
            else
            {
                for (int i = 0; i < 16; i++)
                {
                    if (EngineGlobals.GraphicsDevice.Textures[i] == texture)
                    {
                        EngineGlobals.GraphicsDevice.Textures[i] = null;
                        break;
                    }
                }
            }

            using (MemoryStream memoryStream = new MemoryStream())
            {
                image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);

                memoryStream.Seek(0, SeekOrigin.Begin);

                texture = Texture2D.FromStream(EngineGlobals.GraphicsDevice,
                    memoryStream, image.Width, image.Height, false);

                memoryStream.Close();
            }
        }

        /// <summary>
        /// Recursivley finds all of the files in the directory.
        /// </summary>
        /// <param name="directory"></param>
        /// <returns></returns>
        public static List<string> FindAllFiles(string directory)
        {
            List<string> result = new List<string>();
            Stack<string> stack = new Stack<string>();

            stack.Push(directory);

            while (stack.Count > 0)
            {
                string dir = stack.Pop();

                try
                {
                    result.AddRange(Directory.GetFiles(dir, "*.*"));

                    foreach (string dn in Directory.GetDirectories(dir))
                    {
                        stack.Push(dn);
                    }
                }
                catch { }
            }

            return result;
        }

        public static Project GetProject(string contentPath)
        {
            Project contentProject = null;

            foreach (Project project in ProjectCollection.GlobalProjectCollection.LoadedProjects)
            {
                if (project.FullPath == contentPath)
                {
                    contentProject = project;
                    break;
                }
            }

            if (contentProject == null)
            {
                contentProject = new Project(contentPath);
            }
            return contentProject;
        }
    }
}
